﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using WMM.Tapioca.Taxonomy;
using System.Xml.Linq;
using WMM.Tapioca.Utilities;
using System.Net;
using System.IO;
using System.Web;
using System.Reflection;

namespace WMM.Tapioca
{
    /// <summary>
    /// The base class for XBRL document objects (e.g., linkbase, schema, instance).
    /// </summary>
    /// [Serializable]
    public class XbrlDocument
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private DTS dts;

        /// <returns> Returns the Discoverable Taxonomy Set (DTS) this taxonomy schema
        ///         belongs to. </returns>
        public DTS Dts
        {
            get
            {
                return dts;
            }
            set
            {
                dts = value;
            }
        }

        private XDocument thisXDocument = null;

        public XDocument ThisXDocument
        {
            get { return thisXDocument; }
            set { thisXDocument = value; }
        }

        [NonSerialized()]
        internal XmlNamespaceManager namespaceManager = null;

        public XmlNamespaceManager NamespaceManager
        {
            get { return namespaceManager; }
            set { namespaceManager = value; }
        }

        protected string targetNamespace;

        /// <summary>
        /// The TargetNamespace this document.
        /// </summary>
        public string TargetNamespace
        {
            get { return targetNamespace; }
            set { targetNamespace = value; }
        }

        private string pathFile;

        /// <summary>
        /// The full path to the XBRL schema file.
        /// </summary>
        public string PathFile
        {
            get { return pathFile; }
            set { pathFile = value; }
        }

        private string fileName;

        /// <summary>
        /// The name of the XbrlDocument file.
        /// </summary>
        public string FileName
        {
            get { return fileName; }
            protected set { fileName = value; }
        }

        private string docType;

        public string DocType
        {
            get { return docType; }
            set { docType = value; }
        }

        private string baseAttr;

        public string BaseAttr
        {
            get { return baseAttr; }
            set { baseAttr = value; }
        }

        internal XbrlDocument()
        {
 
        }


        public XbrlDocument(DTS dts, string href, string baseUri, string Base, XmlUrlResolver resolver)
		{
            this.dts = dts;

            fileName = GetFileName(href);
            pathFile = GetFullPath(href, baseUri, Base);          

            try
            {
                XmlReaderSettings settings = new XmlReaderSettings();
                settings.DtdProcessing = DtdProcessing.Parse;
                settings.XmlResolver = resolver;     

                XmlReader xmlReader = XmlTextReader.Create(PathFile, settings);

                thisXDocument = XDocument.Load(xmlReader, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);

                baseAttr = (string) thisXDocument.Root.Attribute(XNamespace.Xml + "base") ?? string.Empty;

                docType = thisXDocument.Root.Name.LocalName;

                targetNamespace = thisXDocument.Root.Name.NamespaceName;

                namespaceManager = thisXDocument.CreateNamespaceManager();
            }
            catch (WebException webEx)
            {                
                log.Error("Validation error on schema:" + pathFile, new XbrlException(webEx.Message, webEx));
                throw new XbrlException(webEx.Message.ToString(), webEx);
            }            
		}


        public static XmlReader CloneXmlReader(XmlReader readerToClone) 
        {
            MethodInfo mf = readerToClone.GetType().GetMethod("MemberwiseClone", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            XmlReader clonedReader = (XmlReader)mf.Invoke(readerToClone, null);
            mf = null;

            return clonedReader;
        }


        private string GetFileName(string href)
        {
            string nameFile = href;
            int LastPathSeparator = href.LastIndexOf('/');

            if (LastPathSeparator == -1)
            {
                LastPathSeparator = href.LastIndexOf('\\');

                if (LastPathSeparator != -1)
                {
                    nameFile = href.Substring(LastPathSeparator + 1);
                }
            }

            nameFile = href.Substring(LastPathSeparator + 1);
            return nameFile;
        }


        /// <summary>
        /// The methos GetPath.
        /// </summary>
        public string GetFullPath(string href, string baseUri, string Base)
        {
            int http = href.IndexOf("http");

            if (http != -1)
            {
                return href;
            }

            string fullPath;
            string documentPath;
            int firstPathSeparator = href.IndexOf(System.IO.Path.DirectorySeparatorChar);

            if (firstPathSeparator == -1)
            {
                int lastPathSeparator = baseUri.LastIndexOf(System.IO.Path.DirectorySeparatorChar);

                if (lastPathSeparator == -1)
                {
                    lastPathSeparator = baseUri.LastIndexOf('/');
                }

                Base = Base.Substring(0, Base.LastIndexOf('/') + 1);

                documentPath = baseUri.Substring(0, lastPathSeparator + 1);
                fullPath = documentPath + Base + href;
            }
            else
            {
                int lastPathSeparator = href.LastIndexOf(System.IO.Path.DirectorySeparatorChar);
                documentPath = href.Substring(0, lastPathSeparator + 1);
                fullPath = href;
            }  
          
            return fullPath;
        }


        /// <summary>
        /// The methos that creates the namespaces.
        /// </summary>
        public void CreateNamespaceManager()
        {
            namespaceManager = new XmlNamespaceManager(new NameTable());

            foreach (XAttribute currentAttribute in thisXDocument.Root.Attributes())
            {
                if (currentAttribute.IsNamespaceDeclaration)
                {
                    string localName = currentAttribute.Name.LocalName;

                    if (localName.Equals("xmlns"))
                    {
                        localName = string.Empty;
                    }

                    namespaceManager.AddNamespace(localName, currentAttribute.Value);
                }
            }
        }


    }
}
